import { Injectable } from '@nestjs/common';
import { DbService } from '../../db/db.service';
import CreateDto from './dto/create.dto';
import { DeviceType } from '@app/db/entity/DeviceType';
import UpdateDto from './dto/update.dto';
import * as dayjs from 'dayjs';
import WorkException from '../../exception/work.exception';
import { DataSource, In } from 'typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';

@Injectable()
export class DeviceTypeService {
    constructor(
        private readonly db:DbService,
        private readonly dataSource: DataSource,
    ){}

    async list() {
        return this.db.deviceType.find(null, null, null, null, {sort : 'DESC', id : 'DESC'})
    }

    async options() {
        return this.db.deviceType.find({enable : true}, {id : true, name : true, code : true, aliProduct : true, level : true, parentId : true}, null, null, {sort : 'DESC', id : 'DESC'})
    }

    async add(dto:CreateDto, memberId:number) {
        if(true == await this.db.deviceType.isExistsByWhere({name : dto.name, parentId : dto.parentId})) throw new Error('已存在相同的名称')
        if(true == await this.db.deviceType.isExistsByWhere({code : dto.code, parentId : dto.parentId})) throw new Error('已存在相同的编码')
        
        const data:KeyToValueType<DeviceType> = {
            ...dto
        }

        if(dto.parentId > 0) {
            const parent = await this.db.deviceType.findOne({id : dto.parentId}, {level : true, topId : true})
            if(!parent) return false

            data.level = parent.level + 1
            data.topId = parent.topId == 0 ? dto.parentId : parent.topId
        }

        console.log(data)

        data.createTime = dayjs().unix()
        data.creatorId = memberId


        return this.db.deviceType.insert(data)
    }

    async update(dto:UpdateDto) {
        let fields = Object.keys(dto) as (keyof DeviceType)[]
        if(fields.length == 0) return true

       
        const row = await this.db.deviceType.findOne({ id : dto.id })
        
        
        if(!row) return false

        let updateData:{[key in keyof DeviceType]? : DeviceType[key]} = {}

        for(let k in row) {
            if(k in dto) {
                if(row[k] != dto[k]) {
                    updateData[k] = dto[k]
                }
            }
            
        }

        if(Object.keys(updateData).length == 0) return true

        if(updateData.name && (true == await this.db.deviceType.isExistsByWhere({name : updateData.name, parentId : dto.parentId == undefined ? row.parentId : dto.parentId}))) throw new Error('已存在相同的名称')
        if(updateData.code && (true == await this.db.deviceType.isExistsByWhere({code : updateData.code, parentId : dto.parentId == undefined ? row.parentId : dto.parentId}))) throw new Error('已存在相同的编码')

        updateData.updateTime = dayjs().unix()

        if(updateData.parentId != undefined && updateData.parentId != row.parentId) { //上级分类改变了
            let newTopId = 0
            let level = 0
            if(updateData.parentId == 0) { //变成了顶级分类
                updateData.topId = 0
                newTopId = row.id
                updateData.level = 0
                level = 0 - row.level
            }else {
                const newParent = await this.db.deviceType.findOne({id : dto.parentId}, {id : true, topId : true, level : true})
                if(!newParent) return false

                newTopId = newParent.topId == 0 ? newParent.id : newParent.topId;
                updateData.topId = newTopId
                updateData.level = newParent.level + 1
                level = updateData.level - row.level;
            }

            //查询子分类
            const subRows = await this.db.deviceType.find({topId : row.parentId == 0 ? dto.id : row.topId})

            if(subRows.length > 0) {
                const subIds:number[] = []
                this.querySubCategory(subRows, subIds, dto.id)

                if(updateData.parentId == dto.id || subIds.includes(dto.parentId)) throw new WorkException('请选择正确的上级类型')
                if(subIds.length > 0) {

                    const queryRunner = this.dataSource.createQueryRunner();
                    await queryRunner.connect();
                    await queryRunner.startTransaction();
                    try {

                        const r1 = await queryRunner.manager.update(DeviceType, {id : dto.id}, updateData)
                        if(r1.affected < 1) {
                            console.log('更改类型失败')
                            await queryRunner.rollbackTransaction();
                            return false
                        }

                        const update:QueryDeepPartialEntity<DeviceType> = {
                            topId : newTopId
                        }

                        if(level > 0) {
                            update.level = () => ("`level` + " + level)
                        }else if(level < 0){
                            update.level = () => ("`level` " + level)
                        }

                        const r2 = await queryRunner.manager.update(DeviceType, {id : In(subIds)}, update)

                        if(r2.affected < 1) {
                            console.log('更改子分类失败')
                            await queryRunner.rollbackTransaction();
                            return false
                        }

                        await queryRunner.commitTransaction();
                        return true
                    } catch (err) {
                        await queryRunner.rollbackTransaction();
                    } finally { 
                        await queryRunner.release();
                    }

                    return false
                }
    
            }

        }

        const r = await this.db.deviceType.update({id : dto.id}, updateData)
        return r > 0
    }

    async del(id:number) {
        if((await this.db.deviceType.isExistsByWhere({parentId : id}))) throw new Error('该类型存在子类型，不可删除')
        return this.db.deviceType.delete({id : id})
    }


    public querySubCategory(categories:DeviceType[], ids:number[], pid:number) {

        categories.forEach(cate => {
            if(cate.parentId == pid) {
                ids.push(cate.id)
                this.querySubCategory(categories, ids, cate.id)
            }
        })

    }


    public async paramsField(id:number) {
        const deviceType = await this.db.deviceType.findOne({id}, ['id', 'paramFields'])
        if(!deviceType || !deviceType.paramFields || deviceType.paramFields.length == 0) return null
        const rows = await this.db.deviceParamsField.find({id : In(deviceType.paramFields)}, {id : true, key : true, name : true, inputType : true, unit : true, options : () => true, enable : true})
        return rows.filter(row => row.enable)
    }
}
